use axum::http::{HeaderName, HeaderValue};
use headers::{Error, Header};
use std::str::FromStr;
use std::sync::LazyLock;
use crate::ToError;

#[derive(Clone, Debug, PartialEq)]
pub struct System(pub SystemType);

#[derive(Clone, Debug, PartialEq)]
pub enum SystemType {
    Sys,
    App,
    Other,
}

static TOKEN: LazyLock<HeaderName> = LazyLock::new(|| HeaderName::from_static("system"));
impl Header for System {
    fn name() -> &'static HeaderName {
        &TOKEN
    }

    fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(values: &mut I) -> Result<Self, Error> {
        values
            .next()
            .and_then(|v| v.to_str().ok()?.parse().ok())
            .map(System)
            .to_err(Error::invalid)
    }

    fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {
        let value = self.0.as_str().parse().expect("system a valid HeaderValue");
        values.extend(std::iter::once(value));
    }
}

impl SystemType {
    fn as_str(&self) -> &str {
        match self {
            SystemType::Sys => "sys",
            SystemType::App => "app",
            SystemType::Other => "other",
        }
    }
}

impl FromStr for SystemType {
    type Err = Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.to_lowercase().as_str() {
            "sys" => Ok(SystemType::Sys),
            "app" => Ok(SystemType::App),
            "other" => Ok(SystemType::Other),
            _ => Err(Error::invalid()),
        }
    }
}
